package main

import (
	"fmt"
	"io"
	"log"
	"net/http"
	"os"
	"time"

	"github.com/gin-gonic/gin"
)

// 定义接收数据的结构体
type Login struct {
	// binding:"required"修饰的字段，若接收为空值，则报错，是必须字段
	User     string `form:"user" json:"user" uri:"user" xml:"user" binding:"required"`
	Password string `form:"password" json:"password" uri:"password" xml:"password" binding:"required"`
}

func MiddleWare() gin.HandlerFunc {
	return func(c *gin.Context) {
		fmt.Println("中间件")
		c.Set("middle_a", "middle中间件")
		status := c.Writer.Status()
		fmt.Println("状态码：", status)
		t1 := time.Now()
		c.Next()

		// 中间件执行完后续的逻辑
		fmt.Println("中间件执行完毕，程序耗时：", time.Since(t1))

	}
}

func main() {
	// 日志
	gin.DisableConsoleColor()
	f, _ := os.Create("log/gin.log")
	gin.DefaultWriter = io.MultiWriter(f, os.Stdout)

	// 1.创建路由
	r := gin.Default()
	// 指定加载的模板文件目录
	r.LoadHTMLGlob("temp/**/*")
	r.Static("/assets", "./assets")
	// 2.绑定路由规则，执行的函数
	// gin.Context，封装了request和response
	// 绑定中间件
	// r.Use(MiddleWare())
	r.GET("/", MiddleWare(), func(c *gin.Context) {
		cookie, err := c.Cookie("session")
		if err != nil {
			cookie = "not set"
			c.SetCookie("session", "123456", 3600, "/", "localhost", false, true)
		}
		fmt.Println("cookie:", cookie)
		// c.String(http.StatusOK, "hello World!")
		c.HTML(http.StatusOK, "index/index.html", gin.H{
			"title": "Gin",
			"ce":    "test",
		})
	})

	r.GET("/home", func(c *gin.Context) {
		auth, err := c.Cookie("abc")
		if err != nil {
			c.JSON(http.StatusNonAuthoritativeInfo, gin.H{
				"status": "304",
			})
			return
		}
		c.JSON(http.StatusOK, gin.H{
			"auth": auth,
		})
	})

	r.GET("login", func(c *gin.Context) {
		c.SetCookie("abc", "123456", 3600, "/", "localhost", true, true)
		c.JSON(http.StatusOK, gin.H{
			"auth": "123456",
		})
	})

	r.GET("/index", func(c *gin.Context) {
		// c.Redirect(http.StatusMovedPermanently, "http://www.baidu.com")
		c.HTML(http.StatusOK, "user/index.html", gin.H{
			"title":   "Gin",
			"address": "http://www.baidu.com",
		})
	})

	r.GET("/long_async", func(c *gin.Context) {
		// 使用goroutine时，必须使用context的只读副本
		copyContext := c.Copy()
		// 异步
		go func() {
			time.Sleep(2 * time.Second)
			log.Println("异步任务", copyContext.FullPath())
		}()
	})

	r.GET("/hello", func(c *gin.Context) {
		c.JSON(http.StatusOK, gin.H{
			"message": "hello",
		})
	})
	r.GET("/user/:name/*action", func(c *gin.Context) {
		name := c.Param("name")
		action := c.Param("action")
		q := c.DefaultQuery("q", "some default")
		message := name + " is " + action + " " + q
		c.JSON(http.StatusOK, gin.H{
			"message": message,
		})
	})

	// post表格
	r.POST("/form", func(c *gin.Context) {
		message := c.PostForm("message")
		name := c.DefaultPostForm("name", "anonymous")
		c.JSON(http.StatusOK, gin.H{
			"message": message,
			"name":    name,
		})
	})
	// 上传文件
	r.MaxMultipartMemory = 8 << 20
	r.POST("/upload", func(c *gin.Context) {
		file, err := c.FormFile("file")
		if err != nil {
			c.String(http.StatusBadRequest, "Bad Request")
			return
		}
		c.SaveUploadedFile(file, file.Filename)
		c.String(http.StatusOK, fmt.Sprintf("'%s' uploaded!", file.Filename))
	})
	// 上传多个文件
	r.POST("/uploads", func(c *gin.Context) {
		form, _ := c.MultipartForm()
		files := form.File["files"]
		for _, file := range files {
			log.Println(file.Filename)
			c.SaveUploadedFile(file, file.Filename)
		}
		c.String(http.StatusOK, fmt.Sprintf("%d files uploaded!", len(files)))
	})

	// 路由分组
	v1 := r.Group("/v1")
	{
		v1.GET("login", login)
		v1.GET("submit", func(c *gin.Context) {
			c.JSON(http.StatusOK, gin.H{
				"message": "login end",
			})
		})
	}

	// json绑定
	r.POST("/loginJson", func(c *gin.Context) {
		var json Login
		if err := c.ShouldBindJSON(&json); err != nil {
			c.JSON(http.StatusBadRequest, gin.H{
				"error": err.Error(),
			})
			return
		}
		if json.User != "admin" || json.Password != "123456" {
			c.JSON(http.StatusBadRequest, gin.H{
				"status": "304",
			})
			return
		}
		c.JSON(http.StatusOK, gin.H{
			"status": "200",
		})
	})

	// 表单绑定
	r.POST("/loginForm", func(c *gin.Context) {
		var form Login
		if err := c.Bind(&form); err != nil {
			c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
			return
		}
		if form.User != "admin" || form.Password != "123456" {
			c.JSON(http.StatusBadRequest, gin.H{
				"status": "304",
			})
			return
		}
		c.JSON(http.StatusOK, gin.H{
			"status": "200",
		})
	})
	// url数据解析和绑定
	r.POST("/loginUrl", func(c *gin.Context) {
		var login Login
		if err := c.ShouldBindUri(&login); err != nil {
			c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
			return
		}
		c.JSON(http.StatusOK, gin.H{
			"status": "200",
			"login":  login,
		})
	})

	r.GET("/someData", func(c *gin.Context) {
		c.XML(200, gin.H{
			"message": "some xml",
		})

		// c.YAML(200, gin.H{
		// 	"message": "some yaml",
		// })

		// var login Login
		// if err := c.ShouldBindQuery(&login); err != nil {
		// 	c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
		// 	return
		// }
		// c.ProtoBuf(200, login)
	})

	// 3.监听端口，默认在8080
	// Run("里面不指定端口号默认为8080")
	r.Run(":8000")
}

func login(c *gin.Context) {
	name := c.DefaultQuery("name", "jack")
	c.String(200, fmt.Sprintf("hello %s\n", name))
}

// 处理函数
// func process(conn net.Conn) {
// 	defer conn.Close() // 关闭连接
// 	for {
// 		reader := bufio.NewReader(conn)
// 		var buf [128]byte
// 		n, err := reader.Read(buf[:]) // 读取数据
// 		if err != nil {
// 			fmt.Println("read from client failed, err:", err)
// 			break
// 		}
// 		recvStr := string(buf[:n])
// 		fmt.Println("收到client端发来的数据：", recvStr)
// 		conn.Write([]byte(recvStr)) // 发送数据
// 	}
// }

// func main() {
// 	listen, err := net.Listen("tcp", "127.0.0.1:20000")
// 	if err != nil {
// 		fmt.Println("listen failed, err:", err)
// 		return
// 	}
// 	for {
// 		conn, err := listen.Accept() // 建立连接
// 		if err != nil {
// 			fmt.Println("accept failed, err:", err)
// 			continue
// 		}
// 		go process(conn) // 启动一个goroutine处理连接
// 	}
// }
